﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using PIT.Labs.SharePoint.Extensions.Helper;

namespace PIT.Labs.SharePoint.Extensions
{
    /// <summary>
    /// This class contains "<a href="http://msdn.microsoft.com/en-us/library/bb383977%28v=vs.90%29.aspx">Extension Methods</a>" for the SPList class.
    /// </summary>
    /// <seealso cref="T:Microsoft.SharePoint.SPList"/>
    public static class SPListExtension
    {
        /// <summary>
        /// Adds a user or group to the list with contributing rights.
        /// </summary>
        /// <param name="spList">Represents a SharePoint Foundation list.</param>
        /// <param name="principal">Represents a user or group that get contributing rights on the list.</param>
        [CLSCompliantAttribute(false)]
        public static void AddContributor(this SPList spList, SPPrincipal principal)
        {
            SPRoleDefinition roleDefinition = spList.ParentWeb.RoleDefinitions.GetByType(SPRoleType.Contributor);
            SPRoleAssignment roleAssignment = new SPRoleAssignment(principal);
            roleAssignment.RoleDefinitionBindings.Add(roleDefinition);
            bool oldState = spList.ParentWeb.AllowUnsafeUpdates;
            spList.ParentWeb.AllowUnsafeUpdates = true;
            spList.RoleAssignments.Add(roleAssignment);
            spList.ParentWeb.AllowUnsafeUpdates = oldState;
        }

        /// <summary>
        /// Creates unique role assignments for the object, optionally copies role assignments from the parent, and optionally clears unique role assignments from descendants. Allows updates via HTTP-GET (Caution: XSS-Attacks)!
        /// </summary>
        /// <param name="spList">Represents a SharePoint Foundation list.</param>
        /// <param name="copyRoleAssignments">
        /// <see cref="Boolean"/><br />
        /// <b>true</b> to copy role assignments from the parent object to this object; <b>false</b> to assign only the current user to security roles.
        /// </param>
        /// <param name="clearSubscopes">
        /// <see cref="Boolean"/><br />
        /// <b>true</b> to clear the unique role assignments from child objects, which in turn inherit role assignments from this object; <b>false</b> to keep the unique role assignments defined on child objects in place.
        /// </param>
        /// <remarks>
        /// This method replaces the default role assignments that the securable object inherits from its parent object with unique role assignments.
        /// </remarks>
        [CLSCompliantAttribute(false)]
        public static void UnsafeBreakRoleInheritance(this SPList spList, bool copyRoleAssignments, bool clearSubscopes)
        {
            if (spList != null)
            {
                bool oldState = spList.ParentWeb.AllowUnsafeUpdates;
                spList.ParentWeb.AllowUnsafeUpdates = true;
                spList.BreakRoleInheritance(copyRoleAssignments, clearSubscopes);
                spList.ParentWeb.AllowUnsafeUpdates = oldState;
            }
        }

        /// <summary>
        /// Creates unique role assignments for the object and optionally copies role assignments from the parent. Allows updates via HTTP-GET (Caution: XSS-Attacks)!
        /// </summary>
        /// <param name="spList">Represents a SharePoint Foundation list.</param>
        /// <param name="copyRoleAssignments">
        /// <see cref="Boolean"/><br />
        /// <b>true</b> to copy role assignments from the parent object to this object; <b>false</b> to assign only the current user to security roles.
        /// </param>
        [CLSCompliantAttribute(false)]
        public static void UnsafeBreakRoleInheritance(this SPList spList, bool copyRoleAssignments)
        {
            if (spList != null)
            {
                bool oldState = spList.ParentWeb.AllowUnsafeUpdates;
                spList.ParentWeb.AllowUnsafeUpdates = true;
                spList.BreakRoleInheritance(copyRoleAssignments);
                spList.ParentWeb.AllowUnsafeUpdates = oldState;
            }
        }


        /// <summary>
        /// Looks in a list based on the specified field for the elements that match the search criteria (NOT case-sensitive).
        /// </summary>
        /// <param name="spList">The list which should be searched.</param>
        /// <param name="spFieldId">The GUID of the SPField which should be searched.</param>
        /// <param name="value">The search value.</param>
        /// <returns>A <see cref="IEnumerable{T}" /> of <see cref="T:Microsoft.SharePoint.SPListItem" /> containing the found list items.</returns>
        /// <example>
        /// The following example shows how to find list items based on the title field:
        /// <code>
        /// <![CDATA[
        /// SPList currentCalendar = SPContext.Current.Web.Lists["Kalender"];
        /// IEnumerable<SPListItem> foundItemsWithTestTitle = currentCalendar.FindItems(SPListHelper.TitleFieldGuid, "Test");
        /// ]]>
        /// </code>
        /// </example>
        [CLSCompliantAttribute(false)]
        public static IEnumerable<SPListItem> FindItems(this SPList spList, Guid spFieldId, String value)
        {
            return FindItems(spList, spFieldId, value, false);
        }

        /// <summary>
        /// Looks in a list based on the specified field for the elements that match the search criteria.
        /// </summary>
        /// <param name="spList">The list which should be searched.</param>
        /// <param name="spFieldId">The GUID of the SPField which should be searched.</param>
        /// <param name="value">The search value.</param>
        /// <param name="isCaseSensitive">If <b>true</b>, the search is case-sensitive.</param>
        /// <returns>A <see cref="IEnumerable{T}" /> of <see cref="T:Microsoft.SharePoint.SPListItem" /> containing the found list items.</returns>
        [CLSCompliantAttribute(false)]
        public static IEnumerable<SPListItem> FindItems(this SPList spList, Guid spFieldId, String value, bool isCaseSensitive)
        {
            if (spFieldId == null)
                throw new ArgumentNullException("spFieldId");

            List<SPListItem> result = new List<SPListItem>();
            SPListItemCollection listItems = spList.Items;
            if (listItems != null)
            {
                StringComparison comparison;
                if (isCaseSensitive)
                    comparison = StringComparison.Ordinal;
                else
                    comparison = StringComparison.OrdinalIgnoreCase;
                foreach (SPListItem li in listItems)
                {
                    if (li[spFieldId] != null)
                    {
                        String itemValue = null;
                        if (li[spFieldId].GetType() == typeof(String))
                            itemValue = (String)li[spFieldId];
                        else
                        {
                            Object itemObject = li[spFieldId];
                            itemValue = Convert.ToString(itemObject, System.Globalization.CultureInfo.InvariantCulture);
                        }
                        if (String.Compare(itemValue, value, comparison) == 0)
                        {
                            result.Add(li);
                        }
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// Looks in a list based on the specified field for the elements that match the search criteria (NOT case-sensitive).
        /// </summary>
        /// <param name="spList">The list which should be searched.</param>
        /// <param name="spFieldName">A string that contains either the display name or the internal name of the field which should be searched.</param>
        /// <param name="value">The search value.</param>
        /// <returns>A <see cref="IEnumerable{T}" /> of <see cref="T:Microsoft.SharePoint.SPListItem" /> containing the found list items.</returns>
        /// <exception cref="System.ArgumentException">The field is not existing</exception>
        /// <example>
        /// The following example shows how to find the calendar items with a specified event date:
        /// <code>
        /// <![CDATA[
        /// SPList currentCalendar = SPContext.Current.Web.Lists["Kalender"];
        /// DateTime dateTime = new DateTime(2012, 1, 27, 10, 0, 0);
        /// IEnumerable<SPListItem> foundItemsWithSpecificEventDate = currentCalendar.FindItems("EventDate", dateTime.ToString(System.Globalization.CultureInfo.InvariantCulture));
        /// ]]>
        /// </code>
        /// </example>
        [CLSCompliantAttribute(false)]
        public static IEnumerable<SPListItem> FindItems(this SPList spList, String spFieldName, String value)
        {
            return FindItems(spList, spFieldName, value, false);
        }

        /// <summary>
        /// Looks in a list based on the specified field for the elements that match the search criteria.
        /// </summary>
        /// <param name="spList">The list which should be searched.</param>
        /// <param name="spFieldName">A string that contains either the display name or the internal name of the field which should be searched.</param>
        /// <param name="value">The search value.</param>
        /// <param name="isCaseSensitive">If <b>true</b>, the search is case-sensitive.</param>
        /// <returns>A <see cref="IEnumerable{T}" /> of <see cref="T:Microsoft.SharePoint.SPListItem" /> containing the found list items.</returns>
        /// <exception cref="System.ArgumentException">The field is not existing</exception>
        [CLSCompliantAttribute(false)]
        public static IEnumerable<SPListItem> FindItems(this SPList spList, String spFieldName, String value, bool isCaseSensitive)
        {
            if (!spList.Fields.ContainsField(spFieldName))
                throw new ArgumentException("Field cannot be found", "spFieldName");
            SPField spField = spList.Fields.GetField(spFieldName);
            return FindItems(spList, spField.Id, value, isCaseSensitive);
        }

        /// <summary>
        /// Looks in a list based on the specified field for the elements that match the search criteria (NOT case-sensitive).
        /// </summary>
        /// <param name="spList">The list which should be searched.</param>
        /// <param name="spField">The field which should be searched.</param>
        /// <param name="value">The search value.</param>
        /// <returns>A <see cref="IEnumerable{T}" /> of <see cref="T:Microsoft.SharePoint.SPListItem" /> containing the found list items.</returns>
        [CLSCompliantAttribute(false)]
        public static IEnumerable<SPListItem> FindItems(this SPList spList, SPField spField, String value)
        {
            return FindItems(spList, spField, value, false);
        }

        /// <summary>
        /// Looks in a list based on the specified field for the elements that match the search criteria.
        /// </summary>
        /// <param name="spList">The list which should be searched.</param>
        /// <param name="spField">The field which should be searched.</param>
        /// <param name="value">The search value.</param>
        /// <param name="isCaseSensitive">If <b>true</b>, the search is case-sensitive.</param>
        /// <returns>A <see cref="IEnumerable{T}" /> of <see cref="T:Microsoft.SharePoint.SPListItem" /> containing the found list items.</returns>
        [CLSCompliantAttribute(false)]
        public static IEnumerable<SPListItem> FindItems(this SPList spList, SPField spField, String value, bool isCaseSensitive)
        {
            if (spField == null)
                throw new ArgumentNullException("spField");
            return FindItems(spList, spField.Id, value, isCaseSensitive);
        }

        /// <summary>
        /// Gets the items of a list.
        /// </summary>
        /// <param name="spList">The list.</param>
        /// <returns>A <see cref="IEnumerable{T}" /> of <see cref="T:Microsoft.SharePoint.SPListItem" /> containing the list items.</returns>
        public static IEnumerable<SPListItem> GetItemsAsIEnumerable(this SPList spList)
        {
            List<SPListItem> items = new List<SPListItem>();
            if (spList.Items != null)
                foreach (SPListItem item in spList.Items)
                {
                    items.Add(item);
                }
            return items;
        }

        /// <summary>
        /// Gets a collection of list items which a certain user has created.
        /// </summary>
        /// <param name="spList">The list that is searched.</param>
        /// <param name="spUser">The SharePoint user that has created the items.</param>
        /// <returns><see cref="T:Microsoft.SharePoint.SPListItemCollection"/><br />The list items that the user has created.</returns>
        [CLSCompliantAttribute(false)]
        public static SPListItemCollection GetItemsCreatedByUser(this SPList spList, SPUser spUser)
        {
            if (spUser == null) throw new ArgumentNullException("spUser");

            SPListItemCollection spListItemCollection = null;
            SPQuery spQuery = new SPQuery();
            StringBuilder queryString = new StringBuilder(String.Empty);
            queryString.Append(@"<Where>");
            queryString.Append(@"   <Eq>");
            queryString.Append(@"       <FieldRef ID=""" + SPListHelper.CreatedByFieldGuid + @""" LookupId=""True"" />");
            queryString.Append(@"       <Value Type=""Lookup"">" + spUser.ID + @"</Value>");
            queryString.Append(@"   </Eq>");
            queryString.Append(@"</Where>");
            spQuery.Query = queryString.ToString();
            spListItemCollection = spList.GetItems(spQuery);
            return spListItemCollection;
        }

        /// <summary>
        /// Updates the database with changes that are made to the list. Allows updates via HTTP-GET (Caution: XSS-Attacks)!
        /// </summary>
        /// <param name="spList">Represents a SharePoint list.</param>
        [CLSCompliantAttribute(false)]
        public static void UnsafeUpdate(this SPList spList)
        {
            if (spList != null)
            {
                bool oldState = spList.ParentWeb.AllowUnsafeUpdates;
                spList.ParentWeb.AllowUnsafeUpdates = true;
                spList.Update();
                spList.ParentWeb.AllowUnsafeUpdates = oldState;
            }
        }
    }
}
